using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;
using XmlDependent;
using System.Xml;

namespace LevelDesigner
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public static int Width
        {
            get { return 800; }//1920; }
        }

        public static int Height
        {
            get { return 600; }//1080; }
        }

        private List<Sprite> CurrentList
        {
            get { return placingInWorldOne ? spriteList1 : spriteList2; }
        }

        public List<Sprite> decorativeList;


        private const float TRANSITION_ALPHA = 0.4f;

        public Vector2 mouseClickPosition;
        public string toPlaceOnClick;
        public List<Sprite> spriteList1, spriteList2;
        private bool placingInWorldOne = true;
        public bool leftMouseClickedOnce;
        public bool rightMouseClickedOnce;
        public bool sClickedOnce;
        private bool lClickedOnce;
        private bool d1ClickedOnce;
        private bool d2ClickedOnce;
        private bool d3ClickedOnce;
        private bool gClickedOnce;
        private bool spaceClickedOnce;
        public bool savingOrLoading;
        public bool mouseIsMovingPlacedBlocked;
        public enum Int { START, END, SMALLBLOCK, MEDIUMBLOCK, LARGEBLOCK, BRICK1, BRICK2, BRICK3, BRICK4 };
        public Int currentPlaceType;
        public MouseState currentMouseState, pastMouseState;
        public KeyboardState currentKeyboardState;
        public static Storage Storage;
        public int currentSaveNumber;
        private float cameraXPos, cameraYPos, infoXPos, infoYPos, xCompensator, yCompensator;
        private Matrix cameraTransform;
        private int d1MenuOptions;
        private int d2MenuOptions;
        private int d3MenuOptions;
        private Sprite startFlag1;
        private Sprite startFlag2;
        private Sprite endFlag1;
        private Sprite endFlag2;
        //private SaveWindow saveWindow;
        //private LoadWindow loadWindow;
        private bool showGrid;
        private int xGridPosition;
        private int yGridPosition;

        private TextInput loadInput, saveInput;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = Width;
            graphics.PreferredBackBufferHeight = Height;
            graphics.IsFullScreen = false;
            IsMouseVisible = true;
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            decorativeList = new List<Sprite>();
            cameraXPos = 0;
            cameraYPos = 0;
            infoXPos = 0;
            infoYPos = Height - 40;
            xCompensator = 0;
            yCompensator = 0;
            cameraTransform = Matrix.CreateTranslation(cameraXPos, cameraYPos, 0.0f);
            spriteList1 = new List<Sprite>();
            spriteList2 = new List<Sprite>();
            leftMouseClickedOnce = false;
            rightMouseClickedOnce = false;
            sClickedOnce = false;
            lClickedOnce = false;
            d1ClickedOnce = false;
            d3ClickedOnce = false;
            gClickedOnce = false;
            spaceClickedOnce = false;
            mouseIsMovingPlacedBlocked = false;
            savingOrLoading = false;
            currentSaveNumber = 1;
            d1MenuOptions = 0;
            d3MenuOptions = 0;
            Storage = new Storage();
            Storage.initialize(Content);
            showGrid = false;
            startFlag1 = new Sprite("Start Flag", 0, 536);
            startFlag2 = new Sprite("Start Flag", 0, 536);
            endFlag1 = new Sprite("End Flag", 800, 536);
            endFlag2 = new Sprite("End Flag", 800, 536);
            xGridPosition = 0;
            yGridPosition = 0;

            loadInput = new TextInput("Load a file");
            saveInput = new TextInput("Save a file");
            //load("TestBlockHeights");
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape))
                this.Exit();

            float dT = (float)gameTime.ElapsedGameTime.TotalSeconds;

            if (loadInput.Active)
            {
                loadInput.Update(dT);

                if (loadInput.InputDone) {
                    Load(loadInput.Text);
                    loadInput.Hide();
                }
            }
            else if (saveInput.Active)
            {
                saveInput.Update(dT);

                if (saveInput.InputDone)
                {
                    Save(saveInput.Text);
                    saveInput.Hide();
                }
            }
            else
            {
                ///The logic dealing with the keyboard, getting which item is currently selected to be placed
                currentKeyboardState = Keyboard.GetState();
                if (currentKeyboardState.IsKeyDown(Keys.D))
                {
                    cameraXPos -= 4;
                    infoXPos += 4;
                    xCompensator += 4;
                }
                if (currentKeyboardState.IsKeyDown(Keys.A))
                {
                    if(xCompensator >=4)
                    {
                        cameraXPos += 4;
                        infoXPos -= 4;
                        xCompensator -= 4;
                    }
                }
                if (currentKeyboardState.IsKeyDown(Keys.S))
                {
                        cameraYPos -= 4;
                        infoYPos += 4;
                        yCompensator += 4;
                }
                if (currentKeyboardState.IsKeyDown(Keys.W))
                {
                    if (yCompensator >= 4)
                    {
                        cameraYPos += 4;
                        infoYPos -= 4;
                        yCompensator -= 4;
                    }
                }
                cameraTransform = Matrix.CreateTranslation(cameraXPos, cameraYPos, 0.0f);

                if (currentKeyboardState.IsKeyDown(Keys.G) && !gClickedOnce)
                {
                    gClickedOnce = true;
                    showGrid = !showGrid;
                }
                else if (currentKeyboardState.IsKeyUp(Keys.G) && gClickedOnce)
                    gClickedOnce = false;

                if (currentKeyboardState.IsKeyDown(Keys.S) && (currentKeyboardState.IsKeyDown(Keys.LeftControl) || currentKeyboardState.IsKeyDown(Keys.RightControl)) && !sClickedOnce && !savingOrLoading)
                {
                    bool isStartFlag1 = false;
                    bool isEndFlag1 = false;
                    bool isStartFlag2 = false;
                    bool isEndFlag2 = false;
                    foreach (Sprite sprite in spriteList1)
                    {
                        if (sprite.type.Equals("Start Flag"))
                            isStartFlag1 = true;
                        if (sprite.type.Equals("End Flag"))
                            isEndFlag1 = true;
                    }
                    foreach (Sprite sprite in spriteList2)
                    {
                        if (sprite.type.Equals("Start Flag"))
                            isStartFlag2 = true;
                        if (sprite.type.Equals("End Flag"))
                            isEndFlag2 = true;
                    }
                    if (isStartFlag1 && isEndFlag1 && isStartFlag2 && isEndFlag2)
                    {
                        sClickedOnce = true;
                        //saveWindow = new SaveWindow(this);
                        //saveWindow.Show();
                        saveInput.Show();
                    }
                    else
                    {
                        Console.WriteLine("You lack a start/end flag, cannot save.");
                    }
                }
                else if (currentKeyboardState.IsKeyUp(Keys.S) && sClickedOnce)
                    sClickedOnce = false;

                if (currentKeyboardState.IsKeyDown(Keys.L) && !lClickedOnce && !savingOrLoading)
                {
                    lClickedOnce = true;
                    //loadWindow = new LoadWindow(this);
                    //loadWindow.Show();
                    loadInput.Show();
                }
                else if (currentKeyboardState.IsKeyUp(Keys.L) && lClickedOnce)
                    lClickedOnce = false;

                if (currentKeyboardState.IsKeyDown(Keys.Space) && !spaceClickedOnce)
                {
                    spaceClickedOnce = true;
                    placingInWorldOne = !placingInWorldOne;
                }
                else if (currentKeyboardState.IsKeyUp(Keys.Space))
                    spaceClickedOnce = false;

                #region Current Block Place Logic
                /// Here's the default format for this section of logic (special logic should be added only as needed):
                ///  *** stands for the key you are assigning the block to
                ///  ### stands for the number of options that key has to toggle between
                ///  $$$ stands for how many times the key must be pressed to select that specific block
                /// You will need to create and initialize a variable called ***ClickedOnce to false to prevent extremely rapid toggling
                ///  AND
                /// Add add a short name for it in the Int enum type way up above (___ will stand for it in this example)
                ///if (currentKeyboardState.IsKeyDown(Keys.***) && ***MenuOptions % ### == $$$ && !***ClickedOnce)
                ///{
                ///     currentPlaceType = Int.___;
                ///     ***ClickedOnce = true;
                ///     ***MenuOptions++;
                ///}
                /// (all of the other if statements for that key (following that format above) go here)
                ///else if (currentKeyboardState.IsKeyUp(Keys.***) && ***ClickedOnce
                ///     ***ClickedOnce = false;

                #region Button 1 (d1)
                if (currentKeyboardState.IsKeyDown(Keys.D1) && d1MenuOptions % 2 == 0 && !d1ClickedOnce)
                {
                    currentPlaceType = Int.START;
                    d1ClickedOnce = true;
                    d1MenuOptions++;
                }
                else if (currentKeyboardState.IsKeyDown(Keys.D1) && d1MenuOptions % 2 == 1 && !d1ClickedOnce)
                {
                    currentPlaceType = Int.END;
                    d1ClickedOnce = true;
                    d1MenuOptions++;
                }
                else if (currentKeyboardState.IsKeyUp(Keys.D1) && d1ClickedOnce)
                    d1ClickedOnce = false;
                #endregion

                #region Button 2 (d2)
                if (currentKeyboardState.IsKeyDown(Keys.D2) && d2MenuOptions % 3 == 0 && !d2ClickedOnce)
                {
                    currentPlaceType = Int.SMALLBLOCK;
                    d2ClickedOnce = true;
                    d2MenuOptions++;
                }
                else if (currentKeyboardState.IsKeyDown(Keys.D2) && d2MenuOptions % 3 == 1 && !d2ClickedOnce)
                {
                    currentPlaceType = Int.MEDIUMBLOCK;
                    d2ClickedOnce = true;
                    d2MenuOptions++;
                }
                else if (currentKeyboardState.IsKeyDown(Keys.D2) && d2MenuOptions % 3 == 2 && !d2ClickedOnce)
                {
                    currentPlaceType = Int.LARGEBLOCK;
                    d2ClickedOnce = true;
                    d2MenuOptions++;
                }
                else if (currentKeyboardState.IsKeyUp(Keys.D2) && d2ClickedOnce)
                    d2ClickedOnce = false;
                #endregion

                #region Button 3 (d3)
		        if (currentKeyboardState.IsKeyDown(Keys.D3) && d3MenuOptions % 4 == 0 && !d3ClickedOnce)
		        {
		            currentPlaceType = Int.BRICK1;
	                d3ClickedOnce = true;
                    d3MenuOptions++;
	            }
	            else if (currentKeyboardState.IsKeyDown(Keys.D3) && d3MenuOptions % 4 == 1 && !d3ClickedOnce)
	            {
	                currentPlaceType = Int.BRICK2;
	                d3ClickedOnce = true;
		            d3MenuOptions++;
	            }
		        else if (currentKeyboardState.IsKeyDown(Keys.D3) && d3MenuOptions % 4 == 2 && !d3ClickedOnce)
		        {
		            currentPlaceType = Int.BRICK3;
		            d3ClickedOnce = true;
		            d3MenuOptions++;
		        }
		        else if (currentKeyboardState.IsKeyDown(Keys.D3) && d3MenuOptions % 4 == 3 && !d3ClickedOnce)
		        {
		            currentPlaceType = Int.BRICK4;
		            d3ClickedOnce = true;
		            d3MenuOptions++;
		        }
		        else if (currentKeyboardState.IsKeyUp(Keys.D3) && d3ClickedOnce)
		            d3ClickedOnce = false;
		        #endregion

                #endregion

                

                ///The logic dealing with the mouse, getting its potition when clicked and keeping it from spamming creating blocks/whatevs
                pastMouseState = currentMouseState;
                currentMouseState = Mouse.GetState();

                if (currentKeyboardState.IsKeyDown(Keys.LeftControl) && currentMouseState.RightButton == ButtonState.Pressed)
                {
                    Sprite removeMe = null;
                    foreach (Sprite sprite in decorativeList)
                    {
                        if ((currentMouseState.X + xCompensator >= sprite.position.X && currentMouseState.X + xCompensator <= sprite.position.X + sprite.size.Width) && (currentMouseState.Y + yCompensator >= sprite.position.Y && currentMouseState.Y + yCompensator <= sprite.position.Y + sprite.size.Height))
                        {
                            removeMe = sprite;
                        }
                    }
                    if (removeMe != null)
                        decorativeList.Remove(removeMe);
                }

                if (currentMouseState.RightButton == ButtonState.Pressed)
                {
                    Sprite removeMe = null;
                    foreach (Sprite sprite in CurrentList)
                    {
                        if ((currentMouseState.X + xCompensator >= sprite.position.X && currentMouseState.X + xCompensator <= sprite.position.X + sprite.size.Width) && (currentMouseState.Y + yCompensator >= sprite.position.Y && currentMouseState.Y + yCompensator <= sprite.position.Y + sprite.size.Height))
                        {
                            removeMe = sprite;
                        }
                    }
                    if (removeMe != null)
                        CurrentList.Remove(removeMe);
                }
                if (currentMouseState.LeftButton == ButtonState.Pressed && !savingOrLoading)
                {
                    foreach (Sprite sprite in CurrentList)
                    {
                        if ((currentMouseState.X + xCompensator >= sprite.position.X && currentMouseState.X + xCompensator <= sprite.position.X + sprite.size.Width) && (currentMouseState.Y + yCompensator >= sprite.position.Y && currentMouseState.Y + yCompensator <= sprite.position.Y + sprite.size.Height))
                        {
                            mouseIsMovingPlacedBlocked = true;
                            sprite.position.X += (32 * (int)((currentMouseState.X) / 32) - 32 * (int)((pastMouseState.X) / 32));
                            sprite.position.Y += (32 * (int)((currentMouseState.Y) / 32) - 32 * (int)((pastMouseState.Y) / 32));
                        }
                    }
                }
                if (currentMouseState.LeftButton == ButtonState.Pressed && !leftMouseClickedOnce && !savingOrLoading)
                {
                    leftMouseClickedOnce = true;
                    if (!mouseIsMovingPlacedBlocked)
                    {
                        if (currentPlaceType == Int.SMALLBLOCK)
                        {
                            CurrentList.Add(new Sprite("smallBlock", 1 + 32 * (int)((currentMouseState.X + xCompensator) / 32), 1 + 32 * (int)((currentMouseState.Y + yCompensator) / 32)));
                        }
                        if (currentPlaceType == Int.MEDIUMBLOCK)
                        {
                            CurrentList.Add(new Sprite("mediumBlock", 1 + 32 * (int)((currentMouseState.X + xCompensator) / 32), 1 + 32 * (int)((currentMouseState.Y + yCompensator) / 32)));
                        }
                        if (currentPlaceType == Int.LARGEBLOCK)
                        {
                            CurrentList.Add(new Sprite("largeBlock", 1 + 32 * (int)((currentMouseState.X + xCompensator) / 32), 1 + 32 * (int)((currentMouseState.Y + yCompensator) / 32)));
                        }
                        if (currentPlaceType == Int.START && placingInWorldOne)
                        {
                            spriteList1.Remove(startFlag1);
                            startFlag1.position.X = 1 + 32 * (int)((currentMouseState.X + xCompensator) / 32);
                            startFlag1.position.Y = 1 + 32 * (int)((currentMouseState.Y + yCompensator) / 32);
                            spriteList1.Add(startFlag1);
                        }
                        if (currentPlaceType == Int.END && placingInWorldOne)
                        {
                            spriteList1.Remove(endFlag1);
                            endFlag1.position.X = 1 + 32 * (int)((currentMouseState.X + xCompensator) / 32);
                            endFlag1.position.Y = 1 + 32 * (int)((currentMouseState.Y + yCompensator) / 32);
                            spriteList1.Add(endFlag1);
                        }
                        if (currentPlaceType == Int.START && !placingInWorldOne)
                        {
                            spriteList2.Remove(startFlag2);
                            startFlag2.position.X = 1 + 32 * (int)((currentMouseState.X + xCompensator) / 32);
                            startFlag2.position.Y = 1 + 32 * (int)((currentMouseState.Y + yCompensator) / 32);
                            spriteList2.Add(startFlag2);
                        }
                        if (currentPlaceType == Int.END && !placingInWorldOne)
                        {
                            spriteList2.Remove(endFlag2);
                            endFlag2.position.X = 1 + 32 * (int)((currentMouseState.X + xCompensator) / 32);
                            endFlag2.position.Y = 1 + 32 * (int)((currentMouseState.Y + yCompensator) / 32);
                            spriteList2.Add(endFlag2);
                        }
                        if (currentPlaceType == Int.BRICK1)
	                    {
	                         decorativeList.Add(new Sprite("brick11", ((currentMouseState.X + xCompensator)) - 175, ((currentMouseState.Y + yCompensator)) - 140));
	                    }
	                    if (currentPlaceType == Int.BRICK2)
	                    {
                            decorativeList.Add(new Sprite("brick21", ((currentMouseState.X + xCompensator)) - 175, ((currentMouseState.Y + yCompensator)) - 140));
	                    }
	                    if (currentPlaceType == Int.BRICK3)
	                    {
                            decorativeList.Add(new Sprite("brick31", ((currentMouseState.X + xCompensator)) - 175, ((currentMouseState.Y + yCompensator)) - 140));
	                    }
	                    if (currentPlaceType == Int.BRICK4)
	                    {
                            decorativeList.Add(new Sprite("brick41", ((currentMouseState.X + xCompensator)) - 175, ((currentMouseState.Y + yCompensator)) - 140));
	                    }
                    }
                }
                if (currentMouseState.LeftButton == ButtonState.Released && leftMouseClickedOnce)
                {
                    leftMouseClickedOnce = false;
                    mouseIsMovingPlacedBlocked = false;
                }
            }
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.White);

            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default,
                         RasterizerState.CullCounterClockwise, null, cameraTransform);
            //spriteBatch.Draw(Storage.SkyYellow, new Vector2(-600 + xCompensator, -200 + yCompensator), Color.White);

            ///the bricks
            if (decorativeList != null)
                foreach (Sprite sprite in decorativeList)
                    spriteBatch.Draw(sprite.image, sprite.position, Color.White);

            if (showGrid)
            {
                ///Code to draw grid here.
                for (int i = 0; i < 27; i++)
                {
                    if ((int)yCompensator % 32 == 0)
                        yGridPosition = (int)yCompensator;
                    if(i < 21)
                        spriteBatch.Draw(Storage.HorzGridLine, new Rectangle(xGridPosition-31, yGridPosition + (32 * i)-31, 860, 1), Color.White);
                    if ((int)xCompensator % 32 == 0)
                        xGridPosition = (int)xCompensator;
                    spriteBatch.Draw(Storage.VertGridLine, new Rectangle((xGridPosition + (32 * i))-31, yGridPosition-31, 1, 660), Color.White);
                }
            }

            if (spriteList1 != null)
                foreach (Sprite sprite in spriteList1)
                    spriteBatch.Draw(sprite.image, sprite.position, Color.White * (placingInWorldOne ? 1f : TRANSITION_ALPHA));

            if (spriteList2 != null)
                foreach (Sprite sprite in spriteList2)
                    spriteBatch.Draw(sprite.image, sprite.position, Color.White * (placingInWorldOne ? TRANSITION_ALPHA : 1f));

            spriteBatch.DrawString(Storage.Font, ("(X: " + (currentMouseState.X + xCompensator) + ", Y: " + (currentMouseState.Y + yCompensator) + ")"), new Vector2(infoXPos, infoYPos), Color.Black);
            spriteBatch.DrawString(Storage.Font, "" + currentPlaceType, new Vector2(infoXPos, infoYPos - 20), Color.Black);
            spriteBatch.End();

            if (loadInput.Active)
                loadInput.Draw(spriteBatch);

            if (saveInput.Active)
                saveInput.Draw(spriteBatch);
            
            base.Draw(gameTime);
        }

        public void Save(string str)
        {
            SaveWorld(str + "1", spriteList1);
            SaveWorld(str + "2", spriteList2);
            SaveWorld(str + "Deco", decorativeList);
        }

        private void SaveWorld(string str, List<Sprite> l)
        {
            BlockSpriteData[] bsd = new BlockSpriteData[l.Count];

            for (int i = 0; i < bsd.Length; i++)
                bsd[i] = l[i].ToBlockSpriteData();

            XmlWriterSettings xmlSettings = new XmlWriterSettings();
            xmlSettings.Indent = true;

            using (XmlWriter xmlWriter = XmlWriter.Create("../../../../LevelDesignerContent/" + str + ".xml", xmlSettings))
            {
                IntermediateSerializer.Serialize(xmlWriter, bsd, null);
            }
        }

        public void Load(string str)
        {
            LoadWorld(str + "1", spriteList1);
            LoadWorld(str + "2", spriteList2);
            LoadWorld(str + "Deco", decorativeList);
        }

        private void LoadWorld(string str, List<Sprite> l)
        {
            l.Clear();

            try
            {
                BlockSpriteData[] bsd = Content.Load<BlockSpriteData[]>(str);

                foreach (BlockSpriteData b in bsd)
                {
                    l.Add(new Sprite(b));
                    if (b.type.Equals("Start Flag"))
                        startFlag1 = l.Last<Sprite>();
                    if (b.type.Equals("End Flag"))
                        endFlag1 = l.Last<Sprite>();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("There is no level already created with name: " + str);
                Console.WriteLine(e.Message);
            }
        }

        public Vector2 snapToGrid(float xCoord, float yCoord)
        {


            return new Vector2(0,0);
        }
        /*
         Right click deletes selection (thing curerently moused over)

Level Editor key mappings (/potential block types):

1: Start flag, end flag
2: regular (square) blocks, vary size
3: rectangular blocks, vary size and orientation
4: sloped blocks, vary size and orientation (if sloped blocks are possible, etc)
5: platform, vary size (can jump up through, can't fall down through w/o pressing down)
6: Obsticle blocks (harmful blocks)
7: Derin's invisible trigger blocks - how to implement touching block x spawns/deletes block y, etc???
         */
    }
}
